package org.simplespringframework;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.simplespringframework.annotation.Component;
import org.simplespringframework.annotation.Controller;
import org.simplespringframework.annotation.Repository;
import org.simplespringframework.annotation.Service;
import org.simplespringframework.aop.annotation.Aspect;
import org.simplespringframework.util.ClassUtil;
import org.simplespringframework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {

    /*
     * 用来存放所有被配置标记的目标对象map
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();

    /*
     * 加载Bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION =
            Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);


    /*
     * 容器是否已经加载过bean
     */
    private boolean loaded = false;


    public boolean isLoaded() {
        return loaded;
    }

    /**
     * 获取bean实例数量
     * @return bean实例数量
     */
    public int size() {
        return beanMap.size();
    }

    /**
     * 获取Bean容器实例
     * @return BeanContainer
     */
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;
        ContainerHolder(){
            instance = new BeanContainer();
        }
    }

    /**
     * 扫描加载所有的Bean
     * @param packageName 需要扫面的包路径
     */
    public synchronized void loadBeans(String packageName) {
        // 判断容器是否被加载过
        if (isLoaded()) {
            log.warn("BeanContainer has bean loaded...");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName : " + packageName);
            return;
        }
        for (Class<?> clazz :classSet){
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                // 如果类上标记了定义的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    // 将目标类本身作为键，目标类的实例作为值，放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }


    /**
     * 添加一个class对象及其Bean实例
     * @param clazz clazz
     * @param bean bean对象
     */
    public Object addBean(Class<?> clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    /**
     * 移除一个IOC容器管理对象
     * @param clazz class对象
     * @return 返回删除的bean的实例 没有则返回null
     */
    public Object removeBean(Class<?> clazz) {
        return beanMap.remove(clazz);
    }


    /**
     * 根据class对象获取bean实例
     * @param clazz clazz
     * @return Bean实例
     */
    public Object getBean(Class<?> clazz) {
        return beanMap.get(clazz);
    }

    /**
     * 获取容器管理的所有class对象集合
     * @return set<class></>集合
     */
    public Set<Class<?>> getClasses() {
        return beanMap.keySet();
    }

    /**
     * 获取容器管理的所有bean集合
     * @return set<class></>集合
     */
    public Set<Object> getBeans() {
        return new HashSet<>(beanMap.values());
    }

    /**
     * 根据注解筛选出Bean的class集合
     * @param annotation 注解
     * @return class集合
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
        // 1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("nothing in beanMap...");
            return null;
        }
        // 2。通过注解筛选被注解标记及class对象，并添加到classesSet里
        HashSet<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            // 类是否有相关注解标记
            if (clazz.isAnnotationPresent(annotation)) {
                classSet.add(clazz);
            }
        }
        // 只有当 classSet 里面元素大于 0 才返回 否则返回null
        return classSet.size() > 0 ? classSet : null;
    }



    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass) {
        // 1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("nothing in beanMap...");
            return null;
        }
        // 2。判断keySet里的元素是否是传入的接口或类的子类，如果是就将其添加到classSet里
        HashSet<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            // 判断keySet里的元素是否是传入的接口或类的子类
            // 通过  isAssignableFrom() 方法判断  clazz 是否是 interfaceOrClass的 实现类 或 子类
            if (interfaceOrClass.isAssignableFrom(clazz)) {
                classSet.add(clazz);
            }
        }
        // 只有当 classSet 里面元素大于 0 才返回 否则返回null
        return classSet.size() > 0 ? classSet : null;
    }
}
